package com.xiyu.service.service.market;

import com.xiyu.service.vo.market.marketTemplate.MarketTemplateGetResourceByTemplateIdOutput;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplateGetContentByTemplateIdOutput;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplateSimpleListOutput;
import com.xiyu.service.enums.mailbox.mail.MailboxMailResourceTypeEnum;
import com.xiyu.service.model.infra.file.InfraFile;
import com.xiyu.service.model.market.template.*;
import com.xiyu.service.model.system.user.SystemUser;
import com.xiyu.service.repository.infra.file.InfraFileRepository;
import com.xiyu.service.service.system.user.UserService;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplateSingleGetOutput;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplatePageQueryOutput;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplatePageQueryInput;
import com.xiyu.service.repository.market.template.MarketTemplateResourceRepository;
import com.xiyu.service.repository.market.template.MarketTemplateContentRepository;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplateUpdateInput;
import com.xiyu.service.repository.market.template.MarketTemplateRepository;
import com.xiyu.service.vo.market.marketTemplate.MarketTemplateCreateInput;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.babyfish.jimmer.Page;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;

import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.babyfish.jimmer.DraftObjects;
import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.market.MarketTemplateConvert;

import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.errorCode.market.MarketTemplateErrorCode.*;
import static com.xiyu.service.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.xiyu.service.util.mail.MailUtils.extractImagePaths;

/**
 * 营销模板 Service 实现类
 */
@Service
@Validated
public class MarketTemplateServiceImpl implements MarketTemplateService {

    @Resource
    private MarketTemplateResourceRepository marketTemplateResourceRepository;

    @Resource
    private MarketTemplateContentRepository marketTemplateContentRepository;

    @Resource
    private MarketTemplateRepository marketTemplateRepository;

    @Resource
    private InfraFileRepository infraFileRepository;

    @Resource
    private UserService userService;




    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(MarketTemplateCreateInput inputVO) {
        MarketTemplate newMarketTemplate = MarketTemplateConvert.INSTANCE.createInputConvert(inputVO);

        MarketTemplate existTemplate = marketTemplateRepository.findByNameAndCreatorId(inputVO.getName(), getLoginUserId()).orElse(null);
        if(existTemplate != null){
            throw exception(MARKET_TEMPLATE_DUPLICATE_NAME);
        }

        // 查找邮件图片资源
        ArrayList<String> imagePaths = extractImagePaths(Objects.requireNonNull(newMarketTemplate.content()).content());
        List<InfraFile> files = infraFileRepository.findByUrls(imagePaths);
        List<MarketTemplateResource> inlineResource = new ArrayList<>();
        for(InfraFile file : files){
            inlineResource.add(MarketTemplateResourceDraft.$.produce(draft ->
                            draft.setResourceType(MailboxMailResourceTypeEnum.INLINE.getValue())
                                    .setFileName(file.name()).setUrlName(file.url())
                    )
            );
        }
        MarketTemplate template = MarketTemplateDraft.$.produce(draft ->
                draft
                        .setName(newMarketTemplate.name())
                        .setSubject(newMarketTemplate.subject())
                        .setType(newMarketTemplate.type())
                        .setResources(Stream.concat(newMarketTemplate.resources().stream(), inlineResource.stream()).collect(Collectors.toList()))
                        .setContent(MarketTemplateContentDraft.$.produce(contentDraft -> contentDraft.setContent(inputVO.getContent().getContent())))
        );

        template = marketTemplateRepository.insert(template);
        return template.id();

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(MarketTemplateUpdateInput inputVO) {
        Optional<MarketTemplate> optionalOldMarketTemplate = marketTemplateRepository.findById(inputVO.getId());
        if(optionalOldMarketTemplate.isEmpty()){
            throw exception(MARKET_TEMPLATE_NOT_EXIST);
        }

        MarketTemplate existTemplate = marketTemplateRepository.findByNameAndCreatorId(inputVO.getName(), getLoginUserId()).orElse(null);
        if(existTemplate != null && existTemplate.id() != inputVO.getId()){
            throw exception(MARKET_TEMPLATE_DUPLICATE_NAME);
        }

        MarketTemplate updateMarketTemplate = MarketTemplateConvert.INSTANCE.updateInputConvert(inputVO);

        // 查找邮件图片资源
        ArrayList<String> imagePaths = extractImagePaths(Objects.requireNonNull(updateMarketTemplate.content()).content());
        List<InfraFile> files = infraFileRepository.findByUrls(imagePaths);
        List<MarketTemplateResource> inlineResource = new ArrayList<>();
        for(InfraFile file : files){
            inlineResource.add(MarketTemplateResourceDraft.$.produce(draft ->
                            draft.setResourceType(MailboxMailResourceTypeEnum.INLINE.getValue())
                                    .setFileName(file.name()).setUrlName(file.url()).setSize(file.size())
                    )
            );
        }
        // 更新附件
        updateMailAttachmentResource(updateMarketTemplate.resources(), inputVO.getId());
        updateMailInlineResource(inlineResource, inputVO.getId());

        updateMarketTemplate = MarketTemplateDraft.$.produce(updateMarketTemplate, draft -> {
            DraftObjects.unload(draft, MarketTemplateProps.RESOURCES);
        });

        marketTemplateRepository.update(updateMarketTemplate);
        return true;
    }

    private void updateMailInlineResource(List<MarketTemplateResource> newResources, Long mailId){
        List<MarketTemplateResource> oldResources = marketTemplateResourceRepository.findByTemplateIdAndResourceType(mailId, MailboxMailResourceTypeEnum.INLINE.getValue());
        if(oldResources.isEmpty() && newResources.isEmpty())
            return;
        if(newResources.isEmpty()){
            marketTemplateResourceRepository.deleteByIds(oldResources.stream().map(MarketTemplateResource::id).collect(Collectors.toList()));
            return;
        }
        for(MarketTemplateResource newResource : newResources){
            if(oldResources.stream().noneMatch(oldResource -> oldResource.urlName().equals(newResource.urlName()))){
                marketTemplateResourceRepository.insert(MarketTemplateResourceDraft.$.produce(newResource, draft -> draft.setTemplateId(mailId)));
            }
        }
        for(MarketTemplateResource oldResource : oldResources){
            if(newResources.stream().noneMatch(newResource -> newResource.urlName().equals(oldResource.urlName()))){
                marketTemplateResourceRepository.deleteById(oldResource.id());
            }
        }
    }

    private void updateMailAttachmentResource(List<MarketTemplateResource> newResources, Long templateId){
        List<MarketTemplateResource> oldResources = marketTemplateResourceRepository.findByTemplateIdAndResourceType(templateId, MailboxMailResourceTypeEnum.ATTACHMENT.getValue());
        if(oldResources.isEmpty() && newResources.isEmpty())
            return;
        if(newResources.isEmpty()){
            marketTemplateResourceRepository.deleteByIds(oldResources.stream().map(MarketTemplateResource::id).collect(Collectors.toList()));
            return;
        }
        if(oldResources.isEmpty()){
            for(MarketTemplateResource newResource : newResources){
                marketTemplateResourceRepository.insert(
                        MarketTemplateResourceDraft.$.produce(newResource, draft -> draft.setTemplateId(templateId))
                );
            }
            return;
        }
        int index = 0, diffIndex = -1;
        for(;index < oldResources.size(); index ++){
            if(newResources.size() <= index
                    || (!newResources.get(index).fileName().equals(oldResources.get(index).fileName()) && !newResources.get(index).urlName().equals(oldResources.get(index).urlName()))){
                if(diffIndex == -1){
                    diffIndex = index;
                }
                marketTemplateResourceRepository.deleteById(oldResources.get(index).id());
            }
        }
        if(diffIndex == -1){
            diffIndex = index;
        }
        for(;diffIndex < newResources.size(); diffIndex++){
            marketTemplateResourceRepository.insert(
                    MarketTemplateResourceDraft.$.produce(newResources.get(diffIndex), draft -> draft.setTemplateId(templateId))
            );
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleted(Long id) {
        Optional<MarketTemplate> optionMarketTemplate = marketTemplateRepository.findDetailById(id);
        if(optionMarketTemplate.isEmpty()){
            throw exception(MARKET_TEMPLATE_NOT_EXIST);
        }
        marketTemplateRepository.deleteById(id);
        return true;
    }

    @Override
    public PageResult<MarketTemplatePageQueryOutput> pageQuery(MarketTemplatePageQueryInput inputVO) {
        List<Long> ownerUserIds = userService.getOwnerUserIds(getLoginUserId());
        Page<MarketTemplate> pageMarketTemplate = marketTemplateRepository.pageQuery(inputVO, ownerUserIds);
        List<MarketTemplatePageQueryOutput> listMarketTemplate = MarketTemplateConvert.INSTANCE.pageQueryOutputConvert(pageMarketTemplate.getRows());
        return new PageResult<>(listMarketTemplate, pageMarketTemplate.getTotalRowCount());
    }

    @Override
    public MarketTemplateSingleGetOutput singleGet(Long id) {
        Optional<MarketTemplate> optionMarketTemplate = marketTemplateRepository.findDetailById(id);
        if(optionMarketTemplate.isEmpty()){
            throw exception(MARKET_TEMPLATE_NOT_EXIST);
        }
        return MarketTemplateConvert.INSTANCE.singleGetOutputConvert(optionMarketTemplate.get());

    }

    @Override
    public List<MarketTemplateSimpleListOutput> simpleList() {
        List<Long> ownerUserIds = userService.getOwnerUsers(getLoginUserId()).stream().map(SystemUser::id).collect(Collectors.toList());
        return MarketTemplateConvert.INSTANCE.simpleListOutputConvert(marketTemplateRepository.simpleList(ownerUserIds));
    }

    @Override
    public MarketTemplateGetContentByTemplateIdOutput getContentByTemplateId(Long templateId) {
        return MarketTemplateConvert.INSTANCE.getContentByTemplateIdOutputConvert(marketTemplateContentRepository.findByTemplateId(templateId).orElse(null));
    }

    @Override
    public List<MarketTemplateGetResourceByTemplateIdOutput> getResourceByTemplateId(Long templateId) {
        return MarketTemplateConvert.INSTANCE.getResourceByTemplateIdOutputConvert(marketTemplateResourceRepository.findByTemplateIdAndResourceType(templateId, MailboxMailResourceTypeEnum.ATTACHMENT.getValue()));
    }

}
